Skip to content

06 Docker部署AI Agent实战

前面五篇把Docker的核心概念和工具都学完了。现在来做一个完整的实战——把一个Flask+Redis的Agent服务容器化部署。

这个项目是一个带访问计数的Agent API服务,Flask处理HTTP请求,Redis存储计数。虽然简单,但涵盖了Docker部署的完整流程:Dockerfile编写、Compose编排、健康检查、数据持久化、开发热更新。

一、项目结构

agent-api/
├── src/
│   └── main.py           # Flask应用
├── requirements.txt       # Python依赖
├── Dockerfile             # 镜像构建配置
├── .dockerignore          # 构建排除文件
├── .env                   # 环境变量
├── compose.yaml           # 服务编排
└── infra.yaml             # 基础设施服务

二、应用代码

python
# src/main.py
import os
import redis
from flask import Flask, request, jsonify

app = Flask(__name__)

cache = redis.Redis(
    host=os.getenv("REDIS_HOST", "redis"),
    port=int(os.getenv("REDIS_PORT", "6379")),
)


@app.route("/")
def index():
    count = cache.incr("hits")
    return jsonify({"message": "Agent服务运行中", "visits": count})


@app.route("/health")
def health():
    try:
        cache.ping()
        return jsonify({"status": "healthy"}), 200
    except redis.ConnectionError:
        return jsonify({"status": "unhealthy"}), 500


@app.route("/chat", methods=["POST"])
def chat():
    data = request.get_json()
    user_message = data.get("message", "")
    count = cache.incr("chat_count")
    return jsonify({
        "reply": f"收到你的第{count}条消息: {user_message}",
        "count": count,
    })


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)
text
# requirements.txt
flask
redis

三、编写Dockerfile

dockerfile
FROM python:3.12-alpine

# 创建非root用户
RUN addgroup -S app && adduser -S app -G app

WORKDIR /app

# 先复制依赖文件,利用构建缓存
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

# 再复制源代码
COPY src ./src

# 设置环境变量
ENV PYTHONUNBUFFERED=1

# 切换到非root用户
USER app

EXPOSE 5000

CMD ["python", "src/main.py"]

逐行拆解:

  1. FROM python:3.12-alpine——用Alpine版本,镜像体积小
  2. 创建非root用户——安全最佳实践
  3. 先COPY requirements.txt再COPY src——利用构建缓存,改代码不用重装依赖
  4. PYTHONUNBUFFERED=1——让Python直接输出日志,不缓冲
  5. USER app——以非root用户运行

四、编写.dockerignore

plaintext
.git
.env
__pycache__
*.pyc
.venv
*.md
docker-compose*.yaml
compose.yaml
infra.yaml
Dockerfile

排除不需要打包进镜像的文件,特别是.env(可能包含API Key)和.git(体积大且无用)。

五、编写compose.yaml

yaml
include:
  - path: ./infra.yaml

services:
  web:
    build: .
    ports:
      - "${APP_PORT}:5000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      redis:
        condition: service_healthy
    develop:
      watch:
        - action: sync+restart
          path: ./src
          target: /app/src
        - action: rebuild
          path: requirements.txt
yaml
# infra.yaml
services:
  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

volumes:
  redis-data:
text
# .env
APP_PORT=8000
REDIS_HOST=redis
REDIS_PORT=6379

拆解每个配置项:

配置作用
include: ./infra.yaml把基础设施服务拆到独立文件
build: .用当前目录的Dockerfile构建
ports: "${APP_PORT}:5000"端口映射,值从.env读取
depends_on + healthcheck等Redis就绪后再启动web
develop.watch开发时代码热更新
volumes: redis-dataRedis数据持久化

六、运行和测试

6.1 首次启动

bash
docker compose up

输出类似:

[+] Running 2/2
 ✔ Container agent-api-redis-1  Healthy     0.0s
 ✔ Container agent-api-web-1    Started     0.5s

Compose等Redis健康检查通过后才启动web服务。

6.2 测试接口

bash
# 首页(带访问计数)
curl http://localhost:8000
# {"message":"Agent服务运行中","visits":1}

# 健康检查
curl http://localhost:8000/health
# {"status":"healthy"}

# Agent聊天接口
curl -X POST http://localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "你好"}'
# {"count":1,"reply":"收到你的第1条消息: 你好"}

6.3 验证数据持久化

bash
# 多访问几次,让计数增加
curl http://localhost:8000
curl http://localhost:8000
curl http://localhost:8000

# 停掉服务
docker compose down

# 重新启动
docker compose up -d

# 访问计数还在
curl http://localhost:8000
# {"message":"Agent服务运行中","visits":4}

因为Redis数据存在Volume里,docker compose down不会删除Volume。

6.4 验证开发热更新

docker compose up --watch启动:

bash
docker compose up --watch

修改src/main.py里的返回消息:

python
return jsonify({"message": "Agent服务已更新", "visits": count})

保存后Compose自动同步到容器并重启,刷新页面就能看到新内容,不需要手动重建镜像。

七、查看日志和调试

bash
# 查看所有服务日志
docker compose logs -f

# 只看web服务日志
docker compose logs -f web

# 进入web容器
docker compose exec web sh

# 查看环境变量
docker compose exec web env | grep REDIS

# 测试web能否连通redis
docker compose exec web python -c "import redis; r = redis.Redis(host='redis'); print(r.ping())"
# 输出: True

# 查看redis里的数据
docker compose exec redis redis-cli GET hits

八、生产部署建议

这个实战项目是开发用的配置。生产部署还需要注意:

8.1 关闭Debug模式

python
# 生产环境不要用debug=True
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

用Gunicorn代替Flask内置服务器:

dockerfile
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "src.main:app"]

8.2 不暴露Redis端口

生产环境Redis不需要对外暴露端口,只让web服务通过内部网络访问就行。

8.3 日志收集

yaml
services:
  web:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

8.4 资源限制

yaml
services:
  web:
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M

九、总结

这个实战项目覆盖了Docker部署的核心流程:

步骤用到的知识
编写DockerfileFROM、WORKDIR、COPY、RUN、ENV、USER、CMD
构建缓存优化先COPY依赖文件,再COPY源代码
安全最佳实践非root用户、.dockerignore排除敏感文件
Compose编排services、build、ports、environment
服务依赖depends_on + healthcheck
数据持久化Volume
开发热更新Compose Watch
环境管理.env文件

整个Docker系列到这里就结束了。从基础概念到Dockerfile编写,从镜像优化到Compose编排,从网络存储到实战部署——掌握这些内容,你就能独立完成一个AI Agent服务的容器化部署。